package org.madgecko.basic.dao;

import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.madgecko.basic.model.Pager;
import org.madgecko.basic.model.SystemContext;

public class BaseDao<T> implements IBaseDao<T> {

	@Inject
	private SessionFactory sessionFactory;
	private Class<?> clz;

	public Class<?> getClz() {
		if (clz == null) {
			// 获取泛型的Class对象
			clz = ((Class<?>) (((ParameterizedType) (this.getClass()
					.getGenericSuperclass())).getActualTypeArguments()[0]));
		}
		return clz;
	}

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	@Override
	public T add(T t) {
		getSession().save(t);
		return t;
	}

	@Override
	public T update(T t) {
		getSession().update(t);
        return t;
	}

	public void delete(int id) {
		getSession().delete(this.load(id));
	}

	@Override
	public void delete(T t) {
		getSession().delete(t);
	}

	@Override
	public T load(int id) {
		return (T) getSession().load(getClz(), id);
	}

	public <N extends Object>List<N> list(String hql, Object[] args) {
		return this.list(hql, args, null);
	}

	public <N extends Object>List<N> list(String hql, Object arg) {
		return this.list(hql, new Object[] { arg });
	}

	public <N extends Object>List<N> list(String hql) {
		return this.list(hql, null, null);
	}

	public <N extends Object>List<N> list(String hql, Map<String, Object> alias) {
		return this.list(hql, null, alias);
	}

	private String initSort(String hql) {
		String order = SystemContext.getOrder();
		String sort = SystemContext.getSort();
		
		if (sort != null && !"".equals(sort.trim())) {
			hql += " order by " + sort;
			if ("desc".equalsIgnoreCase(order)) {
				hql += " desc";
			} else {
				hql += " asc";
			}
		}
		return hql;
	}

	@SuppressWarnings("rawtypes")
	private void setAliasParameter(Query query, Map<String, Object> alias) {
		if (alias != null) {
			Set<String> keys = alias.keySet();
			for (String key : keys) {
				Object val = alias.get(key);
				if (val instanceof Collection) {
					query.setParameterList(key, (Collection) val);
				} else {
					query.setParameter(key, val);
				}
			}
		}
	}

	private void setParameter(Query query, Object[] args) {
		if (args != null && args.length > 0) {
			int index = 0;
			for (Object arg : args) {
				query.setParameter(index++, arg);
			}
		}
	}

	public <N extends Object>List<N> list(String hql, Object[] args, Map<String, Object> alias) {
		// hql不会被修改，视频里错误
		String sortedHql = initSort(hql);
		Query query = getSession().createQuery(sortedHql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.list();
	}

	public Pager<T> find(String hql, Object[] args) {
		return this.find(hql, args, null);
	}

	public Pager<T> find(String hql, Object arg) {
		return this.find(hql, new Object[] { arg });
	}

	public Pager<T> find(String hql) {
		return this.find(hql, null, null);
	}

	private String getCountHql(String hql, boolean isHql) {
		int formIndex = hql.indexOf("from");
		String e = hql.substring(formIndex);
		String c = "select count(*) " + e;
		if (isHql) {
			c.replace("fetch", "");
		}
		return c;
	}

	@SuppressWarnings("rawtypes")
	private void setPagers(Query query, Pager pages) {
		Integer pageSize = SystemContext.getPageSize();
		Integer pageOffset = SystemContext.getPageOffset();
		if (null == pageOffset || pageOffset < 0) {
			pageOffset = 0;
		}
		if (null == pageSize || pageSize < 0) {
			// XXX 待写成全局变量
			pageSize = 15;
		}
		pages.setSize(pageSize);
		pages.setOffset(pageOffset);
		query.setFirstResult(pageOffset).setMaxResults(pageSize);
	}

	public Pager<T> find(String hql, Object[] args, Map<String, Object> alias) {
		hql = initSort(hql);
		Query query = getSession().createQuery(hql);
		String countHql = getCountHql(hql, true);
		Query countQuery = getSession().createQuery(countHql);
		setAliasParameter(query, alias);
		setAliasParameter(countQuery, alias);
		setParameter(query, args);
		setParameter(countQuery, args);
		Pager<T> pages = new Pager<T>();
		setPagers(query, pages);
		List<T> datas = query.list();
		pages.setDatas(datas);
		Long total = (Long)countQuery.uniqueResult();
		pages.setTotal(total);
		return pages;
	}

	public Pager<T> findByAlias(String hql, Map<String, Object> alias) {
		return this.find(hql, null, alias);
	}

	public Object queryObject(String hql, Object[] args,
			Map<String, Object> alias) {
		Query query = getSession().createQuery(hql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.uniqueResult();
	}

	public Object queryObject(String hql, Map<String, Object> alias) {
		return this.queryObject(hql, null, alias);
	}

	public Object queryObject(String hql, Object[] args) {
		return this.queryObject(hql, args, null);
	}

	public Object queryObject(String hql, Object arg) {
		return this.queryObject(hql, new Object[] { arg }, null);
	}

	public Object queryObject(String hql) {
		return this.queryObject(hql, null, null);
	}

	public void updateByHql(String hql, Object[] args) {
		Query query = getSession().createQuery(hql);
		setParameter(query, args);
		query.executeUpdate();
	}

	public void updateByHql(String hql, Object arg) {
		this.updateByHql(hql, new Object[] { arg });
	}

	public void updateByHql(String hql) {
		this.updateByHql(hql, null);
	}

	public <N extends Object>List<N> listBySql(String sql, Object[] args, Class<?> clz,
			boolean hasEntity) {
		return this.listBySql(sql, args, null, clz, hasEntity);
	}

	public <N extends Object>List<N> listBySql(String sql, Object arg, Class<?> clz,
			boolean hasEntity) {
		return this.listBySql(sql, new Object[] { arg }, null, clz, hasEntity);
	}

	public <N extends Object>List<N> listBySql(String sql, Class<?> clz,
			boolean hasEntity) {
		return this.listBySql(sql, null, null, clz, hasEntity);
	}

	public <N extends Object>List<N> listBySql(String sql, Object[] args,
			Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		sql = initSort(sql);
		SQLQuery sq = getSession().createSQLQuery(sql);
		setAliasParameter(sq, alias);
		setParameter(sq, args);
		if (hasEntity) {
			sq.addEntity(clz);
		} else {
			sq.setResultTransformer(Transformers.aliasToBean(clz));
		}
		return sq.list();
	}

	public <N extends Object>List<N> listBySql(String sql, Map<String, Object> alias,
			Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, null, alias, clz, hasEntity);
	}

	public <N extends Object>Pager<N> findBySql(String sql, Object[] args,
			Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, args, null, clz, hasEntity);
	}

	public <N extends Object>Pager<N> findBySql(String sql, Object arg, Class<?> clz,
			boolean hasEntity) {
		return this.findBySql(sql, new Object[]{arg}, clz, hasEntity);
	}

	public <N extends Object>Pager<N> findBySql(String sql, Class<?> clz,
			boolean hasEntity) {
		return this.findBySql(sql, null, null, clz, hasEntity);
	}

	public <N extends Object>Pager<N> findBySql(String sql, Object[] args,
			Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		sql = initSort(sql);
		String countSql = getCountHql(sql, false);
		SQLQuery sqlQuery = getSession().createSQLQuery(sql);
		setAliasParameter(sqlQuery, alias);
		setParameter(sqlQuery, args);
		
		SQLQuery countQuery = getSession().createSQLQuery(countSql);
		setAliasParameter(countQuery, alias);
		setParameter(countQuery, args);
		Pager<N> pages = new Pager<N>();
		setPagers(sqlQuery, pages);
		if(hasEntity){
			sqlQuery.addEntity(clz);
		}else{
			sqlQuery.setResultTransformer(Transformers.aliasToBean(clz));
		}
		List<N> datas = sqlQuery.list();
		BigInteger total = (BigInteger)countQuery.uniqueResult();
		pages.setDatas(datas);
		pages.setTotal(total.longValue());
		return pages;
	}

	public <N extends Object>Pager<N> findBySql(String sql, Map<String, Object> alias,
			Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, null, alias, clz, hasEntity);
	}

	/**
	 * 获取主键系列函数
	 * @param hql
	 * @param args
	 * @param alias
	 * @return
	 */
	public List<Integer> listIdByHql(String hql, Object[] args, Map<String, Object> alias){
		Query query = getSession().createQuery(hql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.list();
	}

	public List<Integer> listIdByHql(String hql){
		Query query = getSession().createQuery(hql);
		return query.list();
	}

	public List<Integer> listIdByHql(String hql, Object arg){
		Query query = getSession().createQuery(hql);
		setParameter(query, new Object[]{arg});
		return query.list();
	}

	public List<Integer> listIdByHql(String hql, Object[] args){
		Query query = getSession().createQuery(hql);
		setParameter(query, args);
		return query.list();
	}

//	public <N extends Object>List<N> listByHql(String hql, Object[] args, Map<String, Object> alias) {
//		// hql不会被修改，视频里错误
//		String sortedHql = initSort(hql);
//		Query query = getSession().createQuery(sortedHql);
//		setAliasParameter(query, alias);
//		setParameter(query, args);
//		return query.list();
//	}
//
//	public <N extends Object>List<N> listByHql(String hql, Object[] args) {
//		return this.listByHql(hql, args, null);
//	}
//
//	public <N extends Object>List<N> listByHql(String hql, Object arg) {
//		return this.listByHql(hql, new Object[] { arg });
//	}
//
//	public <N extends Object>List<N> listByHql(String hql) {
//		return this.listByHql(hql, null, null);
//	}
//
//	public <N extends Object>List<N> listByHql(String hql, Map<String, Object> alias) {
//		return this.listByHql(hql, null, alias);
//	}
}
